//! DSL (Domain Specific Language) support for test case definitions
//! 
//! This module provides comprehensive DSL parsing and processing capabilities,
//! supporting both YAML and JSON formats with HttpRunner compatibility.

pub mod parser;
pub mod template;
pub mod variables;
pub mod functions;

// Re-export commonly used types
pub use parser::{DSLParser, TestCaseDefinition, StepDefinition, ConfigDefinition};
pub use template::{TemplateEngine, TemplateContext};
pub use variables::{VariableManager, VariableResolver, VariableScope};
pub use functions::{FunctionRegistry, Function, FunctionCall};

use crate::error::Result;
use serde_json::Value;
use std::collections::HashMap;

/// DSL processing result
#[derive(Debug, Clone)]
pub struct DSLResult {
    /// Parsed test case definition
    pub test_case: TestCaseDefinition,
    
    /// Resolved variables
    pub variables: HashMap<String, Value>,
    
    /// Processing metadata
    pub metadata: DSLMetadata,
}

/// DSL processing metadata
#[derive(Debug, Clone)]
pub struct DSLMetadata {
    /// Source format (yaml/json)
    pub format: String,
    
    /// Processing timestamp
    pub processed_at: i64,
    
    /// Variable resolution count
    pub variable_resolutions: usize,
    
    /// Function call count
    pub function_calls: usize,
    
    /// Warnings during processing
    pub warnings: Vec<String>,
}

/// DSL processor - main entry point for DSL processing
pub struct DSLProcessor {
    parser: DSLParser,
    template_engine: TemplateEngine,
    variable_manager: VariableManager,
}

impl DSLProcessor {
    /// Create a new DSL processor
    pub fn new() -> Self {
        Self {
            parser: DSLParser::new(),
            template_engine: TemplateEngine::new(),
            variable_manager: VariableManager::new(),
        }
    }
    
    /// Process a test case from YAML content
    pub fn process_yaml(&mut self, content: &str) -> Result<DSLResult> {
        let definition = self.parser.parse_yaml(content)?;
        self.process_definition(definition, "yaml")
    }
    
    /// Process a test case from JSON content
    pub fn process_json(&mut self, content: &str) -> Result<DSLResult> {
        let definition = self.parser.parse_json(content)?;
        self.process_definition(definition, "json")
    }
    
    /// Process a test case from file
    pub fn process_file(&mut self, file_path: &str) -> Result<DSLResult> {
        let content = std::fs::read_to_string(file_path)
            .map_err(|e| crate::error::Error::Internal(format!("Failed to read file {}: {}", file_path, e)))?;
        
        if file_path.ends_with(".yaml") || file_path.ends_with(".yml") {
            self.process_yaml(&content)
        } else if file_path.ends_with(".json") {
            self.process_json(&content)
        } else {
            Err(crate::error::Error::Internal(format!("Unsupported file format: {}", file_path)))
        }
    }
    
    /// Process a parsed test case definition
    fn process_definition(&mut self, mut definition: TestCaseDefinition, format: &str) -> Result<DSLResult> {
        let start_time = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_millis() as i64;
        
        let mut metadata = DSLMetadata {
            format: format.to_string(),
            processed_at: start_time,
            variable_resolutions: 0,
            function_calls: 0,
            warnings: Vec::new(),
        };
        
        // Set up variable manager with config variables
        if let Some(ref config_vars) = definition.config.variables {
            self.variable_manager.set_global_variables(config_vars.clone());
        }
        
        // Process template variables in config
        if let Some(ref mut config_vars) = definition.config.variables {
            for (key, value) in config_vars.iter_mut() {
                let mut template_context = template::TemplateContext::new();
                if let Ok(resolved) = self.template_engine.resolve_value_with_context(value, &mut self.variable_manager, &mut template_context) {
                    *value = resolved.clone();
                    // Set the resolved value back to the variable manager
                    self.variable_manager.set_global_variable(key.clone(), resolved);
                    metadata.variable_resolutions += template_context.stats.variables_resolved;
                    metadata.function_calls += template_context.stats.functions_called;
                }
            }
        }
        
        // Process each test step
        for step in &mut definition.teststeps {
            self.process_step(step, &mut metadata)?;
        }
        
        // Collect final variables
        let variables = self.variable_manager.get_all_variables();
        
        Ok(DSLResult {
            test_case: definition,
            variables,
            metadata,
        })
    }
    
    /// Process a single test step
    fn process_step(&mut self, step: &mut StepDefinition, metadata: &mut DSLMetadata) -> Result<()> {
        // Process step variables first
        if let Some(ref mut step_vars) = step.variables {
            // First resolve step variables using current context
            for (_key, value) in step_vars.iter_mut() {
                let mut template_context = template::TemplateContext::new();
                if let Ok(resolved) = self.template_engine.resolve_value_with_context(value, &mut self.variable_manager, &mut template_context) {
                    *value = resolved;
                    metadata.variable_resolutions += template_context.stats.variables_resolved;
                    metadata.function_calls += template_context.stats.functions_called;
                }
            }
            // Then set resolved step variables in manager for use in request processing
            self.variable_manager.set_step_variables(step_vars.clone());
        }
        
        // Process request parameters if present
        if let Some(ref mut request) = step.request {
            self.process_request_params(request, metadata)?;
        }
        
        // Process validation rules
        if let Some(ref mut validate) = step.validate {
            for validation in validate.iter_mut() {
                self.process_validation(validation, metadata)?;
            }
        }
        
        // Process extraction rules
        if let Some(ref mut extract) = step.extract {
            for (key, value) in extract.iter_mut() {
                if let Ok(resolved) = self.template_engine.resolve_value(value, &mut self.variable_manager) {
                    *value = resolved;
                    metadata.variable_resolutions += 1;
                }
            }
        }
        
        Ok(())
    }
    
    /// Process request parameters
    fn process_request_params(&mut self, request: &mut serde_json::Value, metadata: &mut DSLMetadata) -> Result<()> {
        if let Some(obj) = request.as_object_mut() {
            for (_, value) in obj.iter_mut() {
                let mut template_context = template::TemplateContext::new();
                if let Ok(resolved) = self.template_engine.resolve_value_with_context(value, &mut self.variable_manager, &mut template_context) {
                    *value = resolved;
                    metadata.variable_resolutions += template_context.stats.variables_resolved;
                    metadata.function_calls += template_context.stats.functions_called;
                }
            }
        }
        Ok(())
    }
    
    /// Process validation rules
    fn process_validation(&mut self, validation: &mut serde_json::Value, metadata: &mut DSLMetadata) -> Result<()> {
        if let Some(obj) = validation.as_object_mut() {
            for (_, value) in obj.iter_mut() {
                if let Ok(resolved) = self.template_engine.resolve_value(value, &mut self.variable_manager) {
                    *value = resolved;
                    metadata.variable_resolutions += 1;
                }
            }
        }
        Ok(())
    }
    
    /// Get current variable manager
    pub fn variable_manager(&self) -> &VariableManager {
        &self.variable_manager
    }
    
    /// Get mutable variable manager
    pub fn variable_manager_mut(&mut self) -> &mut VariableManager {
        &mut self.variable_manager
    }
}

impl Default for DSLProcessor {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_dsl_processor_creation() {
        let processor = DSLProcessor::new();
        assert!(processor.variable_manager.get_all_variables().is_empty());
    }

    #[test]
    fn test_dsl_processor_yaml_processing() {
        let mut processor = DSLProcessor::new();
        let yaml_content = r#"
config:
  name: "Test Case"
  variables:
    base_url: "https://api.example.com"
    user_id: 123

teststeps:
  - name: "Get User"
    request:
      method: "GET"
      url: "${base_url}/users/${user_id}"
    validate:
      - check: "status_code"
        expect: 200
"#;
        
        let result = processor.process_yaml(yaml_content);
        assert!(result.is_ok());
        
        let dsl_result = result.unwrap();
        assert_eq!(dsl_result.test_case.config.name, "Test Case");
        assert_eq!(dsl_result.test_case.teststeps.len(), 1);
        assert_eq!(dsl_result.metadata.format, "yaml");
    }

    #[test]
    fn test_dsl_processor_json_processing() {
        let mut processor = DSLProcessor::new();
        let json_content = r#"
{
  "config": {
    "name": "Test Case",
    "variables": {
      "base_url": "https://api.example.com",
      "user_id": 123
    }
  },
  "teststeps": [
    {
      "name": "Get User",
      "request": {
        "method": "GET",
        "url": "${base_url}/users/${user_id}"
      },
      "validate": [
        {
          "check": "status_code",
          "expect": 200
        }
      ]
    }
  ]
}
"#;
        
        let result = processor.process_json(json_content);
        assert!(result.is_ok());
        
        let dsl_result = result.unwrap();
        assert_eq!(dsl_result.test_case.config.name, "Test Case");
        assert_eq!(dsl_result.test_case.teststeps.len(), 1);
        assert_eq!(dsl_result.metadata.format, "json");
    }
}
